#include "fs/fs.h"
#include "tools/klib.h"
#include "comm/boot_info.h"
#include "comm/cpu_instr.h"
#include "tools/log.h"  
#include <sys/stat.h>
#include "dev/console.h"
#include "fs/file.h"
#include "dev/dev.h"
#include "core/task.h"
#include "tools/list.h"
#include "ipc/mutex.h"
#include <sys/file.h>
#include "dev/disk.h"
#include "os_cfg.h"

// 文件系统相关
#define FS_TABLE_SIZE 8
static fs_t fs_table[FS_TABLE_SIZE];
static list_t mounted_fs_list;          // 已经挂载的文件系统表
static list_t free_list;                // 空闲的文件系统

// shell进程相关
#define SECTOR_SIZE 512
#define SHELL_FILE_ID 100
static uint8_t SHELL_ADDR[100*1024];
static uint8_t * file_ptr;

// 文件系统回调函数表
extern fs_op_t devfs_op;
extern fs_op_t fatfs_op;

// 主文件系统
static fs_t * root_fs;

const char * path_next_child(const char * path)
{
    const char * s = path;
    ASSERT(*s == '/');
    while(*s && *s++ == '/'){}
    while(*s && *s++ != '/'){}
    return *s ? s : 0;
}

int path_to_num(const char * path, int * num)
{
    int n = 0;
    const char *s = path;
    while(*s)
    {
        ASSERT(*s >= '0' && *s <= '9');
        n = n * 10 + *s - '0';
        s++; 
    }
    *num = n;
    return 0;
} 

static int is_fd_bad(int fd)
{
    if(fd<0 || fd>= TASK_OPEN_FILE_MAX)
    {
        log_printf("fd is not valid. fd = %d\n", fd);
        return 1;
    }
    return 0;
}

static int is_path_valid(const char * path)
{
    if(!path || path[0] == '\0')
    {
        return 0;
    }
    return 1;
}

static int is_path_begin_with(const char * path, const char * mount_point)
{
    const char *s1 = path, *s2 = mount_point;
    while (*s1 && *s2 && (*s1==*s2))
    {
        *s1++;*s2++;
    }
    
    return (*s2=='\0');
}

static void fs_protect(fs_t *fs)
{
    if(fs->mutex) mutex_lock(fs->mutex);
} 

static void fs_unprotect(fs_t *fs)
{
    if(fs->mutex) mutex_unlock(fs->mutex);
} 

int sys_open(const char * name, int flags, ...)
{
    // 分配文件描述符
    int fd = -1;
    file_t * file = file_alloc();
    if(!file) goto sys_open_failed;
    
    // 分配进程持有的打开文件表项
    fd = task_alloc_fd(file);
    if(fd < 0) goto sys_open_failed;
    
    // 根据当前路径查找对应的文件系统
    fs_t *fs = 0;
    list_node_t * curr = list_first(&mounted_fs_list);
    while (curr)
    {
        fs_t *fs_tmp = list_node_parent(curr, fs_t, node);
        if(is_path_begin_with(name, fs_tmp->mount_point))
        {
            fs = fs_tmp;
            break; 
        } 
        curr = list_node_next(curr);
    }

    if(!fs) 
    {
        fs = root_fs;
    }else{
        // 根据当前路径查找文件系统下的设备名和设备minor
        name = path_next_child(name);
    }

	file->mode = flags;
	file->fs = fs;
	kernel_strncpy(file->name, name, FILE_NAME_SIZE);
    fs_protect(fs); 
    int err = fs->op->open(fs, name, file);
    if(err < 0)
    {
        fs_unprotect(fs);
        log_printf("file system is not mounted. fs = %s\n", name);
        goto sys_open_failed;   
    }
    fs_unprotect(fs);
    return fd;
sys_open_failed:
    if(file) file_free(file);
    if(fd >= 0) task_free_fd(fd);
    return -1;
}

// 从全局文件指针往ptr处写len个字节的数据
int sys_read(int file, char * ptr, int len)
{
    if(is_fd_bad(file) || !ptr || !len) return 0;

    file_t * read_file = task_get_file(file);
    if(!read_file)
    {
        log_printf("file is not opened. fd = %d", file);
        return -1;
    }

    if(read_file->mode == O_WRONLY)
    {
        log_printf("file is write only. fd = %d", file);
        return -1;
    }

    fs_t * fs = read_file->fs;
    fs_protect(fs);
    int err = fs->op->read(ptr, len, read_file);
    fs_unprotect(fs);
    return err;
}

int sys_write(int file, char * ptr, int len)
{
    if(is_fd_bad(file) || !ptr || !len) return 0;

    file_t * write_file = task_get_file(file);
    if(!write_file)
    {
        log_printf("file is not opened. fd = %d", file);
        return -1;
    }

    if(write_file->mode == O_RDONLY)
    {
        log_printf("file is write only. fd = %d", file);
        return -1;
    }
    
    fs_t * fs = write_file->fs;
    fs_protect(fs);
    int err = fs->op->write(ptr, len, write_file);
    fs_unprotect(fs);
    return err;
}

// 移动文件指针
int sys_lseek(int file, int ptr, int dir)
{
    if(is_fd_bad(file)) return 0;

    file_t * seek_file = task_get_file(file);
    if(!seek_file)
    {
        log_printf("file is not opened. fd = %d", file);
        return -1;
    }
    
    fs_t * fs = seek_file->fs;
    fs_protect(fs);
    int err = fs->op->seek(seek_file, ptr, dir);
    fs_unprotect(fs);
    return err;
}

int sys_close(int file)
{
    // if(is_fd_bad(file)) return -1;

    // file_t * close_file = task_get_file(file);
    // ASSERT(close_file->ref > 0);

    // if(close_file->ref-- == 1)
    // {
    //     fs_t * fs = close_file->fs;
    //     fs_protect(fs);
    //     fs->op->close(close_file);
    //     fs_unprotect(fs);
    //     file_free(close_file);
    // }
    // task_free_fd(file);
    // return 0;
    if (is_fd_bad(file)) {
		log_printf("file error");
		return -1;
	}

	file_t * p_file = task_get_file(file);
	if (p_file == (file_t *)0) {
		log_printf("file not opened. %d", file);
		return -1;
	}

	ASSERT(p_file->ref > 0);

	if (p_file->ref-- == 1) {
		fs_t * fs = p_file->fs;

		fs_protect(fs);
		fs->op->close(p_file);
		fs_unprotect(fs);
	    file_free(p_file);
	}

	task_free_fd(file);
	return 0;
}

int sys_isatty(int file)
{
    if(is_fd_bad(file)) return 0;
    file_t * p_file = task_get_file(file);

    return p_file->type == FILE_TTY;    
}

int sys_fstat(int file, struct stat * st)
{
    if(is_fd_bad(file)) return -1;
    file_t * p_file = task_get_file(file);

    kernel_memset((void *)st, 0, sizeof(struct stat));
    fs_t * fs = p_file->fs;
    fs_protect(fs);
    int err = fs->op->stat(p_file, st);
    fs_unprotect(fs);
    return err;
}

int sys_ioctl(int file, int cmd, int arg0, int arg1)
{
    if(is_fd_bad(file)) return -1;
    file_t * p_file = task_get_file(file);

    if(!p_file)
    {
        log_printf("file is not opened. fd = %d", file);
        return -1;
    }
    
    fs_t * fs = p_file->fs;
    fs_protect(fs);
    int err = fs->op->ioctl(p_file, cmd, arg0, arg1);
    fs_unprotect(fs);
    return err;
}

int sys_dup(int file)
{
    if(is_fd_bad(file)) return -1;

    file_t * f = task_get_file(file);
    if(!f)
    {
        log_printf("file is not opend. fd = %d", file);
        return -1;
    }
    int fd = task_alloc_fd(f);
    if(fd >= 0)
    {
        file_inc_ref(f);
        return fd;
    }
    log_printf("no task file avalible.");
    return -1;
}

static fs_op_t * get_fs_op(fs_type_t type)
{
    if(type == FS_DEVFS)
    {
        return &(devfs_op);
    }
    else if(type == FS_FAT16)
    {
        return &(fatfs_op);
    }
}

// 挂载文件系统
static fs_t * mount(fs_type_t type, char * mount_point, int dev_major, int dev_minor)
{
    fs_t * fs = 0;
    log_printf("mount file system: %s, dev: %d\n", mount_point, dev_major);

    // 检查fs是否已经挂载
    list_node_t * curr = list_first(&mounted_fs_list);
    while (curr)
    {
        fs_t * fs_tmp = list_node_parent(curr, fs_t, node);
        if(!kernel_strcmp(fs_tmp->mount_point, mount_point)) 
        {
            log_printf("mount point has been mounted. mount point = %s\n", mount_point);
            goto mount_failed;
        }
        curr = list_node_next(curr);
    }

    // 查找空闲的fs
    list_node_t * free_fs_node = list_remove_first(&free_list);
    if(!free_fs_node)
    {
        log_printf("no file file system. mount failed");
        goto mount_failed;
    }
    fs = list_node_parent(free_fs_node, fs_t, node);

    // 查找相应类型的文件系统 回调函数表
    fs_op_t * op = get_fs_op(type);
    if(!op)
    {
        log_printf("unsupport file system type: %d.\n", type);
        goto mount_failed;
    }
    
    // 挂载新的file system
    kernel_memset((void *)fs, 0, sizeof(fs_t));
    kernel_strncpy(fs->mount_point, mount_point, FS_MOUNT_POINT_SIZE);
    fs->op = op;
    if(op->mount(fs, dev_major, dev_minor) < 0)
    {
        log_printf("mount fs failed. mount point = %s", mount_point);
    }
    
    list_insert_last(&mounted_fs_list, &fs->node);
    return fs;

mount_failed:
    if(fs)
    {
        list_insert_first(&free_list, &(fs->node)); 
    }
    return 0;
}

static int fs_list_init()
{
    list_init(&free_list);
    list_init(&mounted_fs_list);
    for(int i=0; i<FS_TABLE_SIZE; i++)
    {
        list_insert_last(&free_list, &(fs_table[i].node));
    } 
}

void fs_init(void)
{
    fs_list_init();
    file_tabel_init(); 
    disk_init();

    // 设备文件系统，管理多个tty设备，但其他调用时会传入major和minor,所以挂载时不需要
    fs_t * dev_fs = mount(FS_DEVFS, "/dev", 0, 0);
    ASSERT(dev_fs != 0);

    // 磁盘文件系统，管理多个文件组织形式，需要确定一个主系统
    root_fs = mount(FS_FAT16, "/home", OS_ROOT_DEV);
    ASSERT(root_fs != 0);
}

int sys_opendir(const char * path, DIR * dir)
{
    fs_protect(root_fs);
    int err = root_fs->op->opendir(root_fs, path, dir);
    fs_unprotect(root_fs);
    return err;
}

int sys_readdir(DIR * dir, struct dirent * dirent)
{
    fs_protect(root_fs);
    int err = root_fs->op->readdir(root_fs, dir, dirent);
    fs_unprotect(root_fs);
    return err;
}

int sys_closedir(DIR * dir)
{
    fs_protect(root_fs);
    int err = root_fs->op->closedir(root_fs, dir);
    fs_unprotect(root_fs);
    return err;
}

int sys_unlink(const char * name)
{
    fs_protect(root_fs);
    int err = root_fs->op->unlink(root_fs, name);
    fs_unprotect(root_fs);
    return err;
}